package com.fly.feishu.web.config;

import com.alibaba.fastjson.JSONObject;
import com.fly.feishu.api.constant.FeishuApiConstants;
import com.fly.feishu.api.dto.WebAccessToken;
import com.fly.feishu.api.dto.WebAccessTokenCondition;
import com.fly.feishu.api.service.FeishuTokenService;
import com.fly.feishu.web.constant.FeishuWebConstant;
import com.fly.feishu.web.helper.FeishuUserContext;
import com.fly.feishu.web.model.CheckResult;
import com.fly.feishu.web.model.response.FeishuResponse;
import com.fly.feishu.web.utils.CookieUtils;
import com.fly.feishu.web.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Component
@Slf4j
public class LoginIntercept implements HandlerInterceptor {
    @Autowired
    private FeishuTokenService feishuTokenService;

    /**
     * 在请求处理之前进行调用（Controller方法调用之前）
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        String code = request.getParameter("code");
        String state = request.getParameter("state");

        // 飞书认证回掉
        if (StringUtils.equals(state, FeishuWebConstant.FEISHU_STATE_CODE) && StringUtils.isNotEmpty(code)) {
            log.info("飞书sso回调, code = {}, state = {}", code, state);
            WebAccessTokenCondition tokenCondition = WebAccessTokenCondition.builder()
                    .code(code)
                    .grantType(FeishuApiConstants.WEB_TOKEN_GRANT_TYPE)
                    .build();
            WebAccessToken webAccessToken = feishuTokenService.getWebAccessToken(tokenCondition);
            if (webAccessToken != null && StringUtils.isNotEmpty(webAccessToken.getAccessToken())) {
                // 有效时间五分钟
                String jwt = JwtUtils.createJWT(webAccessToken.getRefreshToken(), JSONObject.toJSONString(webAccessToken), 5 * 60 * 1000);

                // 设置用户token
                CookieUtils.setCookie(response, FeishuWebConstant.TOKEN_NAME, jwt, 4 * 60);
                print(response, FeishuResponse.ok("success"));
                return false;
            }
        }


        String tokenHeader = CookieUtils.getCookie(request, FeishuWebConstant.TOKEN_NAME);
        if (StringUtils.isEmpty(tokenHeader)) {
            log.warn("token为空, 验证失败");
            if (isAjax(request)) {
                print(response, FeishuResponse.error(FeishuWebConstant.JWT_ERRCODE_NULL, "签名验证不存在"));
            } else {
                response.sendRedirect(FeishuWebConstant.LOGIN_URL);
            }
            return false;
        } else {
            //验证JWT的签名，返回CheckResult对象
            CheckResult checkResult = JwtUtils.validateJWT(tokenHeader);
            if (checkResult.isSuccess()) {
                Claims jwt = JwtUtils.parseJWT(tokenHeader);
                WebAccessToken webAccessToken = JSONObject.parseObject(jwt.getSubject(), WebAccessToken.class);
                FeishuUserContext.setWebAccessTokenThreadLocal(webAccessToken);
                refreshToken(jwt.getId(), request, response);
                return true;
            } else {
                switch (checkResult.getErrCode()) {
                    // 签名验证不通过
                    case FeishuWebConstant.JWT_ERRCODE_FAIL: {
                        log.info("签名验证不通过");
                        if (isAjax(request)) {
                            print(response, FeishuResponse.error(checkResult.getErrCode(), "签名验证不通过"));
                        } else {
                            response.sendRedirect(FeishuWebConstant.LOGIN_URL);
                        }
                        break;
                    }
                    // 签名过期，返回过期提示码
                    case FeishuWebConstant.JWT_ERRCODE_EXPIRE: {
                        log.info("签名验证不通过");
                        if (isAjax(request)) {
                            print(response, FeishuResponse.error(checkResult.getErrCode(), "签名过期"));
                        } else {
                            response.sendRedirect(FeishuWebConstant.LOGIN_URL);
                        }
                        break;
                    }
                    default:
                        break;
                }
            }
        }
        return false;
    }

    /**
     * 请求处理之后进行调用，但是在视图被渲染之前（Controller方法调用之后）
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {

    }

    /**
     * 在整个请求结束之后被调用，也就是在DispatcherServlet 渲染了对应的视图之后执行（主要是用于进行资源清理工作）
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        FeishuUserContext.clear();
    }

    /**
     * 刷新飞书token
     *
     * @param refreshToken 来自请求身份验证流程，用户扫码登录后会自动302到redirect_uri并带上此参数
     * @param request
     * @param response
     */
    public void refreshToken(String refreshToken, HttpServletRequest request, HttpServletResponse response) {
        log.info("飞书token刷新, code = {}", refreshToken);
        WebAccessToken webAccessToken = feishuTokenService.refreshWebAccessToken(refreshToken);
        if (webAccessToken != null && StringUtils.isNotEmpty(webAccessToken.getAccessToken())) {
            // 有效时间五分钟
            String jwt = JwtUtils.createJWT(webAccessToken.getRefreshToken(), JSONObject.toJSONString(webAccessToken), 5 * 60 * 1000);

            CookieUtils.clearCookie(request, response, FeishuWebConstant.TOKEN_NAME);
            // 设置用户token
            CookieUtils.setCookie(response, FeishuWebConstant.TOKEN_NAME, jwt, 4 * 60);
        }
    }

    public void print(HttpServletResponse response, Object message) throws IOException {
        try {
            response.setStatus(HttpStatus.OK.value());
            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            response.setHeader("Cache-Control", "no-cache, must-revalidate");
            PrintWriter writer = response.getWriter();
            writer.print(JSONObject.toJSONString(message));
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
//        try {
//            response.setStatus(HttpStatus.OK.value());
//            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
//            response.setHeader("Cache-Control", "no-cache, must-revalidate");
//            OutputStream out = response.getOutputStream();
//            byte[] b = new byte[1024];
//            int len = 0;
//            InputStream inputStream = new ByteArrayInputStream(message.toString().getBytes(StandardCharsets.UTF_8));
//            while ((len = inputStream.read(b)) > 0) {
//                out.write(b, 0, len);
//            }
//            inputStream.close();
//            out.flush();
//            out.close();
//            response.flushBuffer();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
    }

    /**
     * 判断是否为ajax请求，默认不是
     *
     * @param request
     * @return
     */
    private boolean isAjax(HttpServletRequest request) {
        if (!org.springframework.util.StringUtils.isEmpty(request.getHeader("x-requested-with")) && request.getHeader("x-requested-with").equals("XMLHttpRequest")) {
            return true;
        }
        return false;
    }
}
